package org.gpp.proj1.ui.gui.editor;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

import org.gpp.proj1.logic.metadata.PartCategory;
import org.gpp.proj1.logic.metadata.PartEntry;
import org.gpp.proj1.logic.metadata.RobotEntry;
import org.gpp.proj1.logic.metadata.RobotPartCategory;
import org.gpp.proj1.ui.gui.SRGGUIView;
import org.gpp.proj1.ui.gui.command.SRGGUICommandFactory;
import org.gpp.proj1.ui.gui.editor.selector.PartSelectorDialog;

/**
 * Dialog used for creating a new robot or edit an existing one
 *  
 * @author Paolo Victor, paolovictor@gmail.com
 */
public class RobotBuilderDialog extends JDialog implements ActionListener {

	private static final long serialVersionUID = 7836627590268639016L;

	/**
	 * Command that opens the head part selector dialog
	 */
	private static final String CHANGE_HEAD_COMMAND = "change" +  PartCategory.HEAD;
	
	/**
	 * Command that opens the legs part selector dialog
	 */
	private static final String CHANGE_LEGS_COMMAND = "change" +  PartCategory.LEGS;
	
	/**
	 * Command that opens the torso part selector dialog
	 */
	private static final String CHANGE_TORSO_COMMAND = "change" +  PartCategory.TORSO;
	
	/**
	 * Command that opens the right arm part selector dialog
	 */
	private static final String CHANGE_RIGHT_ARM_COMMAND = "changeRight" +  PartCategory.ARM;
	
	/**
	 * Command that opens the left arm part selector dialog
	 */
	private static final String CHANGE_LEFT_ARM_COMMAND = "changeLeft" +  PartCategory.ARM;

	/**
	 * Identifier for a part that hasn't been set
	 */
	private static final String UNDEFINED_PART = "None";

	/**
	 * Default identifier for the robot
	 */
	private static final String UNDEFINED_NAME = "Unknown";

	/**
	 * Cancels the build proccess
	 */
	private static final String CANCEL_COMMAND = "cancel";
	
	/**
	 * The parent GUI view
	 */
	private final SRGGUIView view;
	
	/**
	 * The robot entry. It's null if a new robot is being created
	 */
	private RobotEntry robot;
	
	/**
	 * The selected parts
	 */
	private HashMap<RobotPartCategory, PartEntry> selections;
	
	/**
	 * The selected parts
	 */
	private HashMap<RobotPartCategory, PartEntry> originalParts;
	
	private boolean newRobot;
    
	private JButton cancelButton;
    private JButton changeHeadButton;
    private JButton changeLeftArmButton;
    private JButton changeLegsButton;
    private JButton changeRightArmButton;
    private JButton changeTorsoButton;
    private JTextField headField;
    private JLabel headLabel;
    private JTextField leftArmField;
    private JLabel leftArmLabel;
    private JTextField legsField;
    private JLabel legsLabel;
    private JTextField nameField;
    private JLabel nameLabel;
    private JTextField rightArmField;
    private JLabel rightArmLabel;
    private JButton saveButton;
    private JTextField torsoField;
    private JLabel torsoLabel;
    
    /**
     * @param view
     * @param selectedRobot The selected robot. If null, a new robot will be created
     */
    public RobotBuilderDialog( SRGGUIView view, String selectedRobot ) {

    	this.view = view;
    	this.originalParts = new HashMap<RobotPartCategory, PartEntry>();
    	this.selections = new HashMap<RobotPartCategory, PartEntry>();
    	this.newRobot = false;
    	
    	if( selectedRobot != null ) {
    		this.robot = view.getController().getCurrentProfile().getRobot( selectedRobot );
    		
			for( RobotPartCategory category : RobotPartCategory.values() ) {
				if( ! selections.containsKey( category ) ) {
					selections.put( category, robot.getPart( category ) );
					originalParts.put( category, robot.getPart( category ) );
				}
			}
    	}
    	
    	this.initComponents();    	
    	this.setLocationRelativeTo( null );
    }
    
    /**
     * Constructor for a new robot dialog
     * @param view
     */
    public RobotBuilderDialog( SRGGUIView view ) {

    	this( view, null );
    	
    	this.newRobot = true;
    }   

	private void initComponents() {
		
        headField = new JTextField();
        headLabel = new JLabel();
        leftArmLabel = new JLabel();
        leftArmField = new JTextField();
        changeHeadButton = new JButton();
        changeLeftArmButton = new JButton();
        rightArmLabel = new JLabel();
        changeRightArmButton = new JButton();
        rightArmField = new JTextField();
        torsoLabel = new JLabel();
        changeTorsoButton = new JButton();
        torsoField = new JTextField();
        legsLabel = new JLabel();
        changeLegsButton = new JButton();
        legsField = new JTextField();
        saveButton = new JButton();
        cancelButton = new JButton();
        nameField = new JTextField();
        nameLabel = new JLabel();

        setTitle( "Robot Editor" );
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        setResizable( false );
        setModal( true );
        
        headField.setEditable(false);
        headField.setText( this.robot != null ? robot.getPart( RobotPartCategory.HEAD ).getName() : UNDEFINED_PART );
        headField.setBorder(null);

        headLabel.setText("Head");

        leftArmLabel.setText("Left arm");

        leftArmField.setEditable(false);
        leftArmField.setText( this.robot != null ? robot.getPart( RobotPartCategory.ARM_LEFT ).getName() : UNDEFINED_PART );
        leftArmField.setBorder(null);
        
        rightArmLabel.setText("Right arm");

        rightArmField.setEditable(false);
        rightArmField.setText( this.robot != null ? robot.getPart( RobotPartCategory.ARM_RIGHT ).getName() : UNDEFINED_PART );
        rightArmField.setBorder(null);

        torsoLabel.setText("Torso");
        
        torsoField.setEditable(false);
        torsoField.setText( this.robot != null ? robot.getPart( RobotPartCategory.TORSO ).getName() : UNDEFINED_PART );
        torsoField.setBorder(null);

        legsLabel.setText("Legs");


        legsField.setEditable(false);
        legsField.setText( this.robot != null ? robot.getPart( RobotPartCategory.LEGS ).getName() : UNDEFINED_PART );
        legsField.setBorder(null);

        saveButton.setText("Save");
        saveButton.setActionCommand( SRGGUICommandFactory.SAVE_ROBOT_COMMAND );
        saveButton.addActionListener( this );

        cancelButton.setText("Cancel");
        cancelButton.setActionCommand( CANCEL_COMMAND );
        cancelButton.addActionListener( this );

        nameLabel.setText("Name");
        
        nameField.setText( this.robot != null ? robot.getRobotName() : UNDEFINED_NAME );
        if( this.robot != null ) {
        	this.nameField.setEditable( false );
        }

        changeHeadButton.setText("Change");
        changeHeadButton.setActionCommand( CHANGE_HEAD_COMMAND );
        changeHeadButton.addActionListener( this );
        
        changeLeftArmButton.setText("Change");
        changeLeftArmButton.setActionCommand( CHANGE_LEFT_ARM_COMMAND );
        changeLeftArmButton.addActionListener( this );
        
        changeRightArmButton.setText("Change");
        changeRightArmButton.setActionCommand( CHANGE_RIGHT_ARM_COMMAND );
        changeRightArmButton.addActionListener( this );
        
        changeTorsoButton.setText("Change");
        changeTorsoButton.setActionCommand( CHANGE_TORSO_COMMAND );
        changeTorsoButton.addActionListener( this );
        
        changeLegsButton.setText("Change");
        changeLegsButton.setActionCommand( CHANGE_LEGS_COMMAND );
        changeLegsButton.addActionListener( this );

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                        .add(headLabel)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 120, Short.MAX_VALUE)
                        .add(changeHeadButton))
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, nameField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 232, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(nameLabel)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, headField, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 232, Short.MAX_VALUE)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                        .add(torsoLabel)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 115, Short.MAX_VALUE)
                        .add(changeTorsoButton))
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, torsoField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 232, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                        .add(leftArmLabel)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 99, Short.MAX_VALUE)
                        .add(changeLeftArmButton))
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, leftArmField, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 232, Short.MAX_VALUE)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                        .add(rightArmLabel)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 90, Short.MAX_VALUE)
                        .add(changeRightArmButton))
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, rightArmField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 232, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                        .add(legsLabel)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 122, Short.MAX_VALUE)
                        .add(changeLegsButton))
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, legsField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 232, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                        .add(saveButton)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(cancelButton)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(layout.createSequentialGroup()
                        .add(nameLabel)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(nameField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(changeHeadButton)
                            .add(headLabel))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(headField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(changeTorsoButton)
                            .add(torsoLabel))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(torsoField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(changeLeftArmButton)
                            .add(leftArmLabel))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(leftArmField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(changeRightArmButton)
                            .add(rightArmLabel))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(rightArmField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(changeLegsButton)
                            .add(legsLabel))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(legsField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(cancelButton)
                            .add(saveButton))))
                .addContainerGap())
        );
        
        pack();
    }

	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {

		if( e.getActionCommand().equals( CANCEL_COMMAND ) ) {
			this.dispose();
			return;
		} else if( e.getActionCommand().equals( SRGGUICommandFactory.SAVE_ROBOT_COMMAND ) ) {
			if( !areAllFieldsSet() ) {
				JOptionPane.showMessageDialog( this,
						"You haven't selected enough parts to build a robot",
						"Error",
						JOptionPane.ERROR_MESSAGE );
				
			} else if( view.getController().getCurrentProfile().existsRobot( nameField.getText() )
					&& newRobot ) {
				JOptionPane.showMessageDialog( this,
						"There is already a robot with this name",
						"Error",
						JOptionPane.ERROR_MESSAGE );
			} else {
				for( RobotPartCategory category : RobotPartCategory.values() ) {
					if( ! selections.containsKey( category ) ) {
						selections.put( category, originalParts.get( category ) );
					}
				}
				
				view.actionPerformed( e );
				this.dispose();
			}

			return;
		}
		
		// Opening the corresponding dialog
		PartCategory partSelection = null;
		RobotPartCategory robotPartSelection = null;
		JTextField selectionField = null;
		
		List<PartEntry> original = new LinkedList<PartEntry>();
		if( e.getActionCommand().equals( CHANGE_HEAD_COMMAND ) )  {
			partSelection = PartCategory.HEAD;
			robotPartSelection = RobotPartCategory.HEAD;
			selectionField = headField;
			
			if( ! newRobot ) {
				original.add( originalParts.get( RobotPartCategory.HEAD ) );
			}
		} else if( e.getActionCommand().equals( CHANGE_LEGS_COMMAND ) )  {
			partSelection = PartCategory.LEGS;
			robotPartSelection = RobotPartCategory.LEGS;
			selectionField = legsField;
			
			if( ! newRobot ) {
				original.add( originalParts.get( RobotPartCategory.LEGS ) );
			}
		} else if( e.getActionCommand().equals( CHANGE_TORSO_COMMAND ) )  {
			partSelection = PartCategory.TORSO;
			robotPartSelection = RobotPartCategory.TORSO;
			selectionField = torsoField;
			
			if( ! newRobot ) {
				original.add( originalParts.get( RobotPartCategory.TORSO ) );
			}
		} else if( e.getActionCommand().equals( CHANGE_LEFT_ARM_COMMAND ) )  {
			partSelection = PartCategory.ARM;
			robotPartSelection = RobotPartCategory.ARM_LEFT;
			selectionField = leftArmField;
			
			if( ! newRobot ) {
				original.add( originalParts.get( RobotPartCategory.ARM_LEFT ) );
				original.add( originalParts.get( RobotPartCategory.ARM_RIGHT ) );
			}
		} else if( e.getActionCommand().equals( CHANGE_RIGHT_ARM_COMMAND ) )  {
			partSelection = PartCategory.ARM;
			robotPartSelection = RobotPartCategory.ARM_RIGHT;
			selectionField = rightArmField;
			
			if( ! newRobot ) {
				original.add( originalParts.get( RobotPartCategory.ARM_LEFT ) );
				original.add( originalParts.get( RobotPartCategory.ARM_RIGHT ) );
			}
		}

		PartEntry selected = PartSelectorDialog.showDialog( view, partSelection, this.selections.values(), original );
		
		if( selected != null ) {
			selections.put( robotPartSelection, selected );
			selectionField.setText( selected.getName() );
		}
	}

	private boolean areAllFieldsSet() {
		
		return ! ( headField.getText().equals( UNDEFINED_PART ) ||
			legsField.getText().equals( UNDEFINED_PART ) ||
			torsoField.getText().equals( UNDEFINED_PART ) ||
			leftArmField.getText().equals( UNDEFINED_PART ) ||
			rightArmField.getText().equals( UNDEFINED_PART ) );
	}

	/**
	 * @return The selected parts map
	 */
	public Map<RobotPartCategory, PartEntry> getSelections() {

		return this.selections;
	}

	/**
	 * @return The selected robot name
	 */
	public String getNewRobotName() {

		return this.nameField.getText();
	}

	public String getOldRobotName() {
		
		return this.robot == null ? null : robot.getRobotName();
	}                

}
